package com.zzb.orders.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzb.base.constant.MqConstant;
import com.zzb.base.utils.IdWorkerUtils;
import com.zzb.base.utils.QRCodeUtil;
import com.zzb.messagesdk.model.po.MqMessage;
import com.zzb.messagesdk.service.MqMessageService;
import com.zzb.messagesdk.service.impl.MqMessageServiceImpl;
import com.zzb.orders.config.AlipayConfig;
import com.zzb.orders.mapper.XcOrdersGoodsMapper;
import com.zzb.orders.mapper.XcOrdersMapper;
import com.zzb.orders.mapper.XcPayRecordMapper;
import com.zzb.orders.model.dto.AddOrderDto;
import com.zzb.orders.model.dto.PayRecordDto;
import com.zzb.orders.model.dto.PayStatusDto;
import com.zzb.orders.model.po.XcOrders;
import com.zzb.orders.model.po.XcOrdersGoods;
import com.zzb.orders.model.po.XcPayRecord;
import com.zzb.orders.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import static com.alipay.api.AlipayConstants.APP_ID;

/**
 * @Author zzb
 * @Date 2024/7/13 23:38
 * @Version 1.0
 * @description
 */
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<XcOrdersMapper, XcOrders> implements OrderService {
    @Autowired
    private XcOrdersGoodsMapper xcOrdersGoodsMapper;
    @Autowired
    private XcPayRecordMapper xcPayRecordMapper;
    @Value("${pay.qrcodeurl}")
    private String qrcodeurl;
    @Value("${pay.alipay.appId}")
    private String appId;
    @Value("${pay.alipay.privateKey}")
    private String privateKey;
    @Value("${pay.alipay.alipayPublicKey}")
    private String alipayPublicKey;
    @Value("${pay.alipay.notifyUrl}")
    private String notifyUrl;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    private final MqMessageService mqMessageService = new MqMessageServiceImpl();

    @Override
    public PayRecordDto createOrder(String userId, AddOrderDto addOrderDto) {
        // 添加商品订单
        XcOrders orders = saveOrders(userId, addOrderDto);
        // 添加支付交易记录
        XcPayRecord payRecord = createPayRecord(orders);
        // 3. 生成二维码
        String qrCode = null;
        try {
            // 3.1 用订单号填充占位符
            qrcodeurl = String.format(qrcodeurl, payRecord.getPayNo());
            // 3.2 生成二维码
            qrCode = new QRCodeUtil().createQRCode(qrcodeurl, 200, 200);
        } catch (IOException e) {
            throw new RuntimeException("生成二维码出错");
        }
        PayRecordDto payRecordDto = new PayRecordDto();
        BeanUtils.copyProperties(payRecord, payRecordDto);
        payRecordDto.setQrcode(qrCode);
        return payRecordDto;
    }

    @Override
    public PayRecordDto queryPayResult(String payNo) {
        //调用支付宝接口查询支付结果
        PayStatusDto payStatusDto = queryPayResultFromAlipay(payNo);
        //拿到支付结果，更新支付记录表和订单表的状态为 已支付
        XcPayRecord xcPayRecord = saveAlipayStatus(payStatusDto);
        PayRecordDto payRecordDto = new PayRecordDto();
        BeanUtils.copyProperties(xcPayRecord, payRecordDto);
        return payRecordDto;
    }

    @Override
    public void paynotify(HttpServletRequest request, HttpServletResponse response) {
        Map<String, String> params = new HashMap<>();
        Map requestParams = request.getParameterMap();
        log.info("{}", requestParams);
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            params.put(name, valueStr);
        }
        boolean verify_result = false;
        try {
            verify_result = AlipaySignature.rsaCheckV1(params, privateKey, AlipayConfig.CHARSET, AlipayConfig.SIGNTYPE);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        if (verify_result) {
            //商户订单号
            String out_trade_no = null;
            String trade_no = null;
            String trade_status = null;
            String total_amount = null;
            out_trade_no = new String(request.getParameter("out_trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            //支付宝交易号
            trade_no = new String(request.getParameter("trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            //交易状态
            trade_status = new String(request.getParameter("trade_status").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            //付款金额
            total_amount = new String(request.getParameter("total_amount").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            if (trade_status.equals("TRADE_FINISHED")) {   //交易结束

            } else if (trade_status.equals("TRADE_SUCCESS")) {
                // 交易成功，保存订单信息
                PayStatusDto payStatusDto = new PayStatusDto();
                payStatusDto.setOutTradeNo(out_trade_no);
                payStatusDto.setTradeNo(trade_no);
                payStatusDto.setAppId(APP_ID);
                payStatusDto.setTradeStatus(trade_status);
                payStatusDto.setTotalAmount(total_amount);
                this.saveAlipayStatus(payStatusDto);
                log.debug("交易成功");
            }
            try {
                response.getWriter().write("success");
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            try {
                response.getWriter().write("fail");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void notifyPayResult(MqMessage mqMessage) {
        // 将消息体转化为JSON
        String jsonMsg = JSON.toJSONString(mqMessage);
        // 设消息的持久化方式为PERSISTENT，即消息会被持久化到磁盘上，确保即使在RabbitMQ服务器重启后也能够恢复消息
        Message mesObj = MessageBuilder.withBody(jsonMsg.getBytes()).setDeliveryMode(MessageDeliveryMode.PERSISTENT).build();
        // 封装CorrelationData，用于跟踪消息的相关信息
        CorrelationData correlationData = new CorrelationData(mqMessage.getId().toString());
        // 添加一个Callback对象，该对象用于在消息确认时处理消息的结果
        correlationData.getFuture().addCallback(result -> {
            if (result.isAck()) {
                //消息发送成功，删除消息中的记录
                log.info("消息发送成功: {}", jsonMsg);
                mqMessageService.completed(mqMessage.getId());
            } else {
                //消息发送失败
                log.error("消息发送失败 消息Id: {} , 原因: {}", mqMessage.getId(), result.getReason());
            }
        }, ex -> {
            //消息异常
            log.error("消息发送异常: 消息Id: {} , 原因: {}", mqMessage.getId(), ex.getMessage());
        });
        rabbitTemplate.convertAndSend(MqConstant.PAYNOTIFY_EXCHANGE_FANOUT, "", mesObj, correlationData);
    }

    @Override
    public void requestpay(String payNo, HttpServletResponse response) {
        //根据payNo从xc_pay_record表查询
        XcPayRecord xcPayRecord = xcPayRecordMapper
                .selectOne(new LambdaQueryWrapper<XcPayRecord>().eq(XcPayRecord::getPayNo, payNo));
        //获取订单总价单位元
        Float totalPrice = xcPayRecord.getTotalPrice();
        //获取订单名称
        String orderName = xcPayRecord.getOrderName();
        String productCode = "QUICK_WAP_WAY";
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, appId,
                privateKey, AlipayConfig.FORMAT, AlipayConfig.CHARSET,
                alipayPublicKey, AlipayConfig.SIGNTYPE);
        //获得初始化的AlipayClient
        AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();    //创建API对应的request
        alipayRequest.setNotifyUrl(notifyUrl); //在公共参数中设置回跳和通知地址
        String bizContentTemplate = "{" +
                "    \"out_trade_no\":\"%s\"," +
                "    \"total_amount\":%s," +
                "    \"subject\":\"%s\"," +
                "    \"product_code\":\"%s\"" +
                "  }";
        String bizContent = String.format(bizContentTemplate, payNo, totalPrice, orderName, productCode);
        alipayRequest.setBizContent(bizContent);   //填充业务参数
        String form = null;  //调用SDK生成表单
        try {
            form = alipayClient.pageExecute(alipayRequest).getBody();
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        response.setContentType("text/html;charset=" + AlipayConfig.CHARSET);
        try {
            response.getWriter().write(form);  //直接将完整的表单html输出到页面
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private XcPayRecord saveAlipayStatus(PayStatusDto payStatusDto) {
        // 1. 获取支付流水号
        String payNo = payStatusDto.getOutTradeNo();
        // 2. 查询数据库订单状态
        XcPayRecord payRecord = xcPayRecordMapper.selectOne(new QueryWrapper<XcPayRecord>().eq("pay_no", payNo));
        if (payRecord == null) {
            throw new RuntimeException("未找到支付记录");
        }
        XcOrders order = this.getBaseMapper().selectById(payRecord.getOrderId());
        if (order == null) {
            throw new RuntimeException("找不到相关联的订单");
        }
        String statusFromDB = payRecord.getStatus();
        // 2.1 已支付，直接返回
        if ("600002".equals(statusFromDB)) {
            return null;
        }
        // 3. 查询支付宝交易状态
        String tradeStatus = payStatusDto.getTradeStatus();
        // 3.1 支付宝交易已成功，保存订单表和交易记录表，更新交易状态
        if ("TRADE_SUCCESS".equals(tradeStatus)) {
            // 更新支付交易表
            payRecord.setStatus("601002");
            //设置第三方支付平台流水号
            payRecord.setOutPayNo(payStatusDto.getTradeNo());
            payRecord.setOutPayChannel("Alipay");
            payRecord.setPaySuccessTime(LocalDateTime.now());
            int updateRecord = xcPayRecordMapper.updateById(payRecord);
            if (updateRecord <= 0) {
                throw new RuntimeException("更新支付交易表失败");
            }
            // 更新订单表
            order.setStatus("600002");
            int updateOrder = this.getBaseMapper().updateById(order);
            if (updateOrder <= 0) {
                log.debug("更新订单表失败");
                throw new RuntimeException("更新订单表失败");
            }
        }
        //将消息添加到数据库中
        MqMessage message = mqMessageService.addMessage("payresult_notify", order.getOutBusinessId(), order.getOrderType(), null);
        //发送支付成功通知给消息队列
        notifyPayResult(message);
        return payRecord;
    }

    private PayStatusDto queryPayResultFromAlipay(String payNo) {
        // 1. 获得初始化的AlipayClient
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, appId,
                privateKey, "json", AlipayConfig.CHARSET,
                alipayPublicKey, AlipayConfig.SIGNTYPE);
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", payNo);
        request.setBizContent(bizContent.toString());
        AlipayTradeQueryResponse response = null;
        // 2. 请求查询
        try {
            response = alipayClient.execute(request);
        } catch (AlipayApiException e) {
            throw new RuntimeException("请求支付宝查询支付结果异常");
        }
        // 3. 查询失败
        if (!response.isSuccess()) {
            throw new RuntimeException("请求支付宝查询支付结果异常");
        }
        // 4. 查询成功，获取结果集
        String resultJson = response.getBody();
        // 4.1 转map
        Map resultMap = JSON.parseObject(resultJson, Map.class);
        // 4.2 获取我们需要的信息
        Map<String, String> alipay_trade_query_response = (Map) resultMap.get("alipay_trade_query_response");
        // 5. 创建返回对象
        PayStatusDto payStatusDto = new PayStatusDto();
        // 6. 封装返回
        String tradeStatus = alipay_trade_query_response.get("trade_status");
        String outTradeNo = alipay_trade_query_response.get("out_trade_no");
        String tradeNo = alipay_trade_query_response.get("trade_no");
        String totalAmount = alipay_trade_query_response.get("total_amount");
        payStatusDto.setTradeStatus(tradeStatus);
        payStatusDto.setOutTradeNo(outTradeNo);
        payStatusDto.setTradeNo(tradeNo);
        payStatusDto.setTotalAmount(totalAmount);
        payStatusDto.setAppId(appId);
        return payStatusDto;
    }

    private XcPayRecord createPayRecord(XcOrders orders) {
        if (orders == null) {
            throw new RuntimeException("订单不存在");
        }
        if ("600002".equals(orders.getStatus())) {
            throw new RuntimeException("订单已支付");
        }
        XcPayRecord payRecord = new XcPayRecord();
        payRecord.setPayNo(String.valueOf(IdWorkerUtils.getInstance().nextId()));
        payRecord.setOrderId(orders.getId());
        payRecord.setOrderName(orders.getOrderName());
        payRecord.setTotalPrice(orders.getTotalPrice());
        payRecord.setCurrency("CNY");
        payRecord.setCreateDate(LocalDateTime.now());
        payRecord.setStatus("601001");  // 未支付
        payRecord.setUserId(orders.getUserId());
        int insert = xcPayRecordMapper.insert(payRecord);
        if (insert <= 0) {
            throw new RuntimeException("插入支付交易记录失败");
        }
        return payRecord;
    }

    private XcOrders saveOrders(String userId, AddOrderDto addOrderDto) {
        // 幂等性判断
        LambdaQueryWrapper<XcOrders> queryWrapper = new LambdaQueryWrapper<XcOrders>()
                .eq(XcOrders::getOutBusinessId, addOrderDto.getOutBusinessId());
        XcOrders order = this.getOne(queryWrapper);
        if (order != null) {
            return order;
        }
        // 2. 插入订单表
        order = new XcOrders();
        BeanUtils.copyProperties(addOrderDto, order);
        order.setId(IdWorkerUtils.getInstance().nextId());
        order.setCreateDate(LocalDateTime.now());
        order.setUserId(userId);
        order.setStatus("600001");
        int insert = this.getBaseMapper().insert(order);
        if (insert <= 0) {
            throw new RuntimeException("插入订单记录失败");
        }
        // 3. 插入订单明细表
        Long orderId = order.getId();
        String orderDetail = addOrderDto.getOrderDetail();
        List<XcOrdersGoods> xcOrdersGoodsList = JSON.parseArray(orderDetail, XcOrdersGoods.class);
        xcOrdersGoodsList.forEach(goods -> {
            goods.setOrderId(orderId);
            int insert1 = xcOrdersGoodsMapper.insert(goods);
            if (insert1 <= 0) {
                throw new RuntimeException("插入订单明细失败");
            }
        });
        return order;
    }
}
